﻿using Common;
using DataBase.Models;
using Orleans;
using Orleans.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Server.Impl
{
    /// <summary>
    /// 某组待命点，注意，这里是某组，而不是所有的待命点
    /// </summary>
    internal class StayGroupImpl : Grain, IStayGroup
    {
        private readonly IPersistentState<List<StayMarker>> cache;
        private readonly ApplicationDBContext db;
         
        public StayGroupImpl([PersistentState("stayGroup", "redisStorage")] IPersistentState<List<StayMarker>> persistent, ApplicationDBContext adb)
        {
            this.cache = persistent;
            db = adb;
        }

        public override async Task OnActivateAsync()
        {
            await cache.ReadStateAsync();
            await base.OnActivateAsync();
        }


        public async Task<bool> 锁定待命点(string MarkerID, long AgvID)
        {
            var obj = cache.State.Find(x => x.MarkerID == MarkerID);
            if (obj == null)
            {
                return false;
            }
            if (obj.AgvID == AgvID)
            {
                return true;
            }
            obj.AgvID = AgvID;
            obj.LockTime = DateTime.Now;
            await cache.WriteStateAsync();

            return true;
        }



        public async Task 全部解锁()
        {
            bool hasChange = false;
            cache.State.ForEach(item =>
            {
                if (item.AgvID != 0)
                {
                    item.AgvID = 0;
                    hasChange = true;
                } 
            });
            if (hasChange)
            {
                await cache.WriteStateAsync();
            }
        }

        public async Task 待命点解锁(string MarkerID)
        {
            var obj = cache.State.Find(x => x.MarkerID == MarkerID);
            if (obj != null)
            {
                obj.AgvID = 0;
                await cache.WriteStateAsync();
            }
        }

        public Task<long> 获取停得最久的AGV()
        {
            var AgvID = cache.State.Where(x => x.AgvID > 0 ).OrderBy(x => x.LockTime).Select(x => x.AgvID).FirstOrDefault();
            return Task.FromResult(AgvID);
        }

        public Task<string?> 获取可用待命点ID()
        {
            var obj = cache.State.Where(x => x.AgvID == 0).OrderBy(x=>x.LockTime).Select(x=>x.MarkerID).FirstOrDefault();
            return Task.FromResult(obj);
        }

        public async Task 重置数据()
        {
            //从数据库加载基础的待命点，同时将现有的数据填上去
            bool changed = false;

            var groupname = this.GetPrimaryKeyString();
            var 待命点集合 = db.markerInfo.Where(x => x.GroupMarkerID == groupname).ToList();
            待命点集合.ForEach(marker =>
            {
                if (cache.State.Any(y => y.MarkerID == marker.MarkerID) == false)
                {
                    //需要新增
                    changed = true;
                    var obj = new StayMarker()
                    {
                        AgvID=0,
                        LockTime=DateTime.Now,
                        MarkerID=marker.MarkerID,
                    };
                    cache.State.Add(obj);
                }
                else
                {
                    //否则不处理
                }
            });

            //去掉库里面没有的但是缓存里面有的
            var dbids = 待命点集合.Select(x => x.MarkerID).ToList();
            if(cache.State.Any(x=> dbids.Contains(x.MarkerID) == false))
            {
                changed = true;
                cache.State.RemoveAll(x=>dbids.Contains(x.MarkerID)==false);
            }
            if (changed)
            {
                await cache.WriteStateAsync();
            } 
        }
    }

    #region 数据定义


    /// <summary>
    /// 每个待命点位
    /// </summary>
    [Serializable]
    internal class StayMarker
    {
        /// <summary>
        /// 地标编号
        /// </summary>
        public string MarkerID { get; set; } = "";

        /// <summary>
        /// 停放的AGV编号。未停放的为0
        /// </summary>
        public long  AgvID { get; set; }

        /// <summary>
        /// AGV开始停放的时间
        /// </summary>
        public DateTime  LockTime { get; set; }
    }

    #endregion
}
